package com.rwhansen.humidor;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Random;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;


public class Database {

	private String name;
	private String owner;
	private ArrayList<Cigar> cigars;
	
	public static final String CIGAR_DB_FILENAME = "RealCigarDatabase.xml";
	public static final String HUMIDOR_DB_FILENAME = "RealHumidorDatabase.xml";
	public static final String RATINGS_DB_FILENAME = "RealRatingsDatabase.xml";
//	public static final String CIGAR_DB_FILENAME = "CigarDatabase.xml";
//	public static final String HUMIDOR_DB_FILENAME = "HumidorDatabase.xml";
//	public static final String RATINGS_DB_FILENAME = "RatingsDatabase.xml";

	public Database(String name) {
		this.name = name;
		owner = "unknown owner";
		cigars = new ArrayList<Cigar>();
	}
	
	public String getOwner() {
		return owner;
	}

	public void setOwner(String owner) {
		this.owner = owner;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public ArrayList<Cigar> getCigars() {
		return cigars;
	}

	public void addCigarToDatabase(Cigar cigar) {
		this.cigars.add(cigar);
	}

	public void removeCigarFromDatabase(Cigar cigar) {
		this.cigars.remove(cigar);		
	}
	
	public void replaceCigarInDatabase(Cigar oldCigar, Cigar newCigar) {
		removeCigarFromDatabase(oldCigar);
		addCigarToDatabase(newCigar);
	}

	private static boolean saveStringToFile(String fileName, String saveString) {
		boolean saved = false;
		BufferedWriter bw = null;
		
		try{
			bw = new BufferedWriter(new FileWriter(fileName));
			
			try {
				bw.write(saveString);
				saved = true;
			}
			finally {
				bw.close();
			}
		}
		catch (IOException ex) {
			ex.printStackTrace();
		}
		return saved;
	}

	private static String getStringFromFile(String fileName) {
		BufferedReader br = null;
		StringBuilder sb = new StringBuilder();
		
		try{
			br = new BufferedReader(new FileReader(fileName));
			try {
				String s;
				while ((s = br.readLine()) != null){
					//add linefeed back since stripped by readline()
					sb.append(s);
					sb.append("\n");
				}
			}
			finally {
				br.close();
			}
		}
		catch (IOException ex) {
			ex.printStackTrace();
		}
		return sb.toString();
	}

	private static String convertDatabaseToXML(Database db) {
		XStream xStream = new XStream(new DomDriver());
		xStream.setMode(XStream.ID_REFERENCES);
		xStream.alias("Cigar", Cigar.class);
		xStream.alias("Database", Database.class);
		return xStream.toXML(db);
	}

	private static Database convertXMLtoDatabase(String XMLString) {
		Database db = null;
		XStream xStream = new XStream(new DomDriver());
		xStream.setMode((XStream.ID_REFERENCES));
		xStream.alias("Cigar", Cigar.class);
		xStream.alias("Database", Database.class);
		Object obj = xStream.fromXML(XMLString);
		if (obj instanceof Database)
		{
			db = (Database) obj;
		}
		return db;
	}

	/**
	 * Commit a database to a file
	 * 
	 * @param fileName - the filename to save the database to
	 * @param db - the database object to save
	 * @return true if successful
	 */
	public static boolean writeDatabase(String fileName, Database db) {
		return saveStringToFile(fileName, convertDatabaseToXML(db));
	}

	/**
	 * Read a database from file
	 * 
	 * @param fileName - the file to read the database from
	 * @return the Database object
	 */
	public static Database readDatabase(String fileName) {
		return convertXMLtoDatabase(getStringFromFile(fileName));
	}

	public static void deleteCigar(int index, String databaseFileName) {
		File file = new File(databaseFileName);
		
		boolean exists = file.exists();
		if (exists) {
			Database db = Database.readDatabase(databaseFileName);
			db.getCigars().remove(index);
			
			Database.writeDatabase(databaseFileName, db);
		}
	}

	public static void setQuantity(int index, String databaseFileName, int quantity) {
		File file = new File(databaseFileName);
		
		boolean exists = file.exists();
		if (exists) {
			Database db = Database.readDatabase(databaseFileName);
			Cigar c23 = db.getCigars().get(index);
			c23.setQuantity(quantity);
			Database.writeDatabase(databaseFileName, db);
		}	
	}
	
	/**
	 * Use to get number of cigars in humidor
	 * @return Number of cigars in database
	 */
	public int getNumberOfCigars() {
		int cigarQuantity = 0;
		for (Cigar cigar : getCigars())
		{
			cigarQuantity = cigarQuantity + cigar.getQuantity();
		}
			
		return cigarQuantity;
	}
	
	/**
	 * Use to get the average of my ratings
	 * @return
	 */
	public double getMyAverageRating() {
		double totalRating = 0.0;
		double totalCigars = 0.0;
		double avgRating = 0.0;
		
		for (Cigar cigar : getCigars())
		{
			totalRating = totalRating + cigar.getMyRating();
			totalCigars = totalCigars + cigar.getQuantity();
		}
		
		avgRating = totalRating / totalCigars;
		
        DecimalFormat twoDForm = new DecimalFormat("#.##");
        
        return Double.valueOf(twoDForm.format(avgRating));
	}
	
	public double getCAAverageRating() {
		double totalRating = 0.0;
		double totalCigars = 0.0;
		double avgRating = 0.0;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getCaRating() != 0) {
				totalRating = totalRating + (cigar.getCaRating() * cigar.getQuantity());
				totalCigars = totalCigars + cigar.getQuantity();
			}
		}	
		avgRating = totalRating / totalCigars;
		DecimalFormat twoDForm = new DecimalFormat("#.##");
        
        return Double.valueOf(twoDForm.format(avgRating));
	}
	
	private static int ROUNDING_MODE = BigDecimal.ROUND_HALF_EVEN;
	private static int DECIMALS = 2;
	
	public BigDecimal getValueInDatabase() {
		BigDecimal totalValue = new BigDecimal(0);
		
		for (Cigar cigar : getCigars())
		{
			for (int j = 0; j < cigar.getQuantity(); j++) {
				totalValue = totalValue.add(cigar.getPrice());
			}
		}
		
		totalValue.setScale(DECIMALS, ROUNDING_MODE);
		
		return totalValue;
	}	
	
	public BigDecimal getAverageCigarValue() {
		double total = 0;
		int count = 0;

		for (Cigar cigar : getCigars())
		{
			for (int j = 0; j < cigar.getQuantity(); j++) {
				total = total + cigar.getPrice().doubleValue();
				count++;
			}
			
		}	
		
		if (count == 0)
		{
			BigDecimal.valueOf(0).setScale(DECIMALS, ROUNDING_MODE);
		}
		
		return BigDecimal.valueOf(total/count).setScale(DECIMALS, ROUNDING_MODE);
	}
	
	public String getMostExpensiveCigarString() {
		BigDecimal price = new BigDecimal(0);
		String name = "ERROR!";
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getPrice().compareTo(price) == 1)
			{
				name = cigar.getName();
				price = cigar.getPrice();
			}
		}
		
		return name + " ($" + price.toString() + ")";
	}
	
	public String getLeastExpensiveCigarString() {
		BigDecimal price = new BigDecimal(100000.00);
		String name = "ERROR!";
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getPrice().compareTo(price) == -1)
			{
				name = cigar.getName();
				price = cigar.getPrice();
			}
		}
		
		return name + " ($" + price.toString() + ")";
	}
	
	public String getCigarHighestQuantity() {
		String name = "ERROR!";
		String date = "ERROR!";
		int qty = 0;
		
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getQuantity() > qty)
			{
				name = cigar.getName();
				qty = cigar.getQuantity();
				date = cigar.getDateAddHumidor();
			}
		}
		
		return name + " [" + date + "] (" + qty + ")";
	}

	/**
	 * This will get the name of the database
	 * @param fileName - File name of the database
	 * @return - Name of the database
	 */
	public static String getDatabaseName(String fileName) {
		File file = new File(fileName);
		
		boolean exists = file.exists();
		if (exists) {
			Database database = Database.readDatabase(fileName);
			return database.getName();
		}
		return "ERROR!";
	}

	/**
	 * This will remove one cigar from the database, if there are multiple of the same cigar (ie. quantity >1) it will not delete the cigar entierly. If the quantity = 1 it will be deleted from the database.
	 * @param index - Index of the cigar in the database.
	 * @param fileName - File name of the database.
	 */
	public static void removeOneCigar(int index, String fileName) {
		File file = new File(fileName);
		
		boolean exists = file.exists();
		if (exists) {
			Database database = Database.readDatabase(fileName);
			Cigar cigar = database.getCigars().get(index);
			cigar.setQuantity(cigar.getQuantity() - 1);
			if (cigar.getQuantity() < 1) {
				database.getCigars().remove(index);
			}
			Database.writeDatabase(fileName, database);
		}
		
	}
	
	/**
	 * Use this to smoke a cigar from the Humidor
	 * 
	 * @param index - Index of the cigar being smoked
	 */
	public static void smokeCigarInHumidor(int index) {
		File databaseFile = new File(HUMIDOR_DB_FILENAME);
		File ratingsFile = new File(RATINGS_DB_FILENAME);
		
		boolean databaseExists = databaseFile.exists();
		boolean ratingsExists = ratingsFile.exists();
		if (databaseExists && ratingsExists) {
			Database humidor = Database.readDatabase(HUMIDOR_DB_FILENAME);
			
			Cigar cigar = humidor.getCigars().get(index);
			cigar.setQuantity(cigar.getQuantity() - 1);
			if (cigar.getQuantity() < 1) {
				humidor.getCigars().remove(index);
			}
			Database.writeDatabase(HUMIDOR_DB_FILENAME, humidor);

			cigar.setQuantity(0);
			cigar.setDateSmoked("T");
			PersonalCigarAssistant.buildRatingDetails(cigar);
			CigarDetailRatingPanel.editFields(true);
			
		}
	}
	
	/**
	 * As of right now it gives the last 30ish days not an exat 30 because of the number of days in each month.
	 * 
	 * @return The number of cigars in the last 30 days
	 */
	public int getCigarsSmokedLastThirtyDays() {
		int cigarsSmoked = 0;
		int monthSmoked = 0;
		int daySmoked = 0;
		
		Calendar cal = Calendar.getInstance();
		int day = cal.get(Calendar.DAY_OF_YEAR);
		int oneMonthAgo = 0;
		
		if (day < 30)
		{
			oneMonthAgo = day + 335;
		}
		else
		{
			oneMonthAgo = day - 31;
		}
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				monthSmoked = Integer.parseInt(cigar.getDateSmoked().substring(5, 7));
				daySmoked = Integer.parseInt(cigar.getDateSmoked().substring(8, 10));
				
				//TODO: Fix this
				daySmoked = (monthSmoked * 30) + daySmoked;
				
				if (daySmoked <= day && daySmoked > oneMonthAgo)
				{
					cigarsSmoked = cigarsSmoked +1;
				}
			}
		}
		
		return cigarsSmoked;
	}
	
	/**
	 * 
	 * @return the number of cigars smoked in the current month
	 */
	public int getCigarsSmokedThisMonth() {
		int cigarsSmoked = 0;
		int yearSmoked = 0;
		int monthSmoked = 0;
		
		Calendar cal = Calendar.getInstance();
		int month = cal.get(Calendar.MONTH) + 1;
		int year = cal.get(Calendar.YEAR);
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				monthSmoked = Integer.parseInt(cigar.getDateSmoked().substring(5, 7));
				
				if (yearSmoked == year && monthSmoked == month)
				{
					cigarsSmoked = cigarsSmoked +1;
				}
			}
		}
		
		return cigarsSmoked;
	}
	
	/**
	 * 
	 * @return the number of cigars smoked last month
	 */
	public int getCigarsSmokedLastMonth() {
		int cigarsSmoked = 0;
		int yearSmoked = 0;
		int monthSmoked = 0;
		
		Calendar cal = Calendar.getInstance();
		int month = cal.get(Calendar.MONTH);
		int year = cal.get(Calendar.YEAR);
		
		if (month == 0) {
			year = year - 1;
			month = 12;
		}
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				monthSmoked = Integer.parseInt(cigar.getDateSmoked().substring(5, 7));
				
				if (yearSmoked == year && monthSmoked == month)
				{
					cigarsSmoked = cigarsSmoked +1;
				}
			}
		}
		
		return cigarsSmoked;
	}
	
	public int getCigarsSmokedthisYear() {
		int cigarsSmoked = 0;
		int yearSmoked = 0;
		
		Calendar cal = Calendar.getInstance();
		int year = cal.get(Calendar.YEAR);

		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				
				if (yearSmoked == year)
				{
					cigarsSmoked = cigarsSmoked +1;
				}
			}
		}
		
		return cigarsSmoked;
	}
	
	public int getCigarsSmokedLastYear() {
		int cigarsSmoked = 0;
		int yearSmoked = 0;
		
		Calendar cal = Calendar.getInstance();
		int lastYear = cal.get(Calendar.YEAR) - 1;

		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				
				if (yearSmoked == lastYear)
				{
					cigarsSmoked = cigarsSmoked +1;
				}
			}
		}
		
		return cigarsSmoked;
	}
	
	public String getFirstCigarRated() {
		String first = "ERROR!";
		int yearSmoked = 3001;
		int monthSmoked = 0;
		int daySmoked = 0;
		
		int year = 3000;
		int month = 13;
		int day = 32;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				monthSmoked = Integer.parseInt(cigar.getDateSmoked().substring(5, 7));
				daySmoked = Integer.parseInt(cigar.getDateSmoked().substring(8, 10));
				
				if (yearSmoked <= year)
				{
					if (yearSmoked < year)
					{
						year = yearSmoked;
						month = 13;
						day = 32;
					}
					if (monthSmoked <= month)
					{
						if (monthSmoked < month)
						{
							month = monthSmoked;
							day = 32;
						}
						if (daySmoked < day)
						{
							first = cigar.getName() + " [" + cigar.getDateSmoked() + "]";
							year = yearSmoked;
							month = monthSmoked;
							day = daySmoked;
						}
					}
				}
			}
		}
		
		return first;
	}
	
	public String getMostRecentCigarSmoked() {
		String recent = "ERROR!";
		int yearSmoked = 0;
		int monthSmoked = 0;
		int daySmoked = 0;
		
		int year = 0;
		int month = 0;
		int day = 0;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateSmoked().charAt(4) == '/' && cigar.getDateSmoked().charAt(7) == '/') {
				yearSmoked = Integer.parseInt(cigar.getDateSmoked().substring(0, 4));
				monthSmoked = Integer.parseInt(cigar.getDateSmoked().substring(5, 7));
				daySmoked = Integer.parseInt(cigar.getDateSmoked().substring(8, 10));
				
				if (yearSmoked >= year)
				{
					if (yearSmoked > year)
					{
						month = 0;
					}
					if (monthSmoked >= month)
					{
						if (monthSmoked > month)
						{
							day = 0;
						}
						if (daySmoked > day)
						{
							recent = cigar.getName() + " [" + cigar.getDateSmoked() + "]";
							year = yearSmoked;
							month = monthSmoked;
							day = daySmoked;
						}
					}
				}
			}
		}
		return recent;
	}
	
	public String getOldestCigar() {
		String oldest = "ERROR!";
		int yearAdded =0;
		int monthAdded = 0;
		int dayAdded = 0;
		
		int year = 3000;
		int month = 13;
		int day = 32;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateAddHumidor().charAt(4) == '/' && cigar.getDateAddHumidor().charAt(7) == '/') {
				yearAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(0, 4));
				monthAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(5, 7));
				dayAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(8, 10));
				
				if (yearAdded <= year)
				{
					if (yearAdded < year)
					{
						year = yearAdded;
						month = 13;
						day = 32;
					}
					if (monthAdded <= month)
					{
						if (monthAdded < month)
						{
							month = monthAdded;
							day = 32;
						}
						if (dayAdded < day)
						{
							oldest = cigar.getName() + " [" + cigar.getDateAddHumidor() + "] (" + cigar.getQuantity() + ")";
							year = yearAdded;
							month = monthAdded;
							day = dayAdded;
						}
					}
				}
			}
		}
		
		return oldest;
	}
	
	public String getNewestCigar() {
		String recent = "ERROR!";
		int yearAdded = 0;
		int monthAdded = 0;
		int dayAdded = 0;
		
		int year = 0;
		int month = 0;
		int day = 0;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getDateAddHumidor().charAt(4) == '/' && cigar.getDateAddHumidor().charAt(7) == '/') {
				yearAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(0, 4));
				monthAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(5, 7));
				dayAdded = Integer.parseInt(cigar.getDateAddHumidor().substring(8, 10));
				
				if (yearAdded >= year)
				{
					if (yearAdded > year)
					{
						month = 0;
					}
					if (monthAdded >= month)
					{
						if (monthAdded > month)
						{
							day = 0;
						}
						if (dayAdded > day)
						{
							recent = cigar.getName() + " [" + cigar.getDateAddHumidor() + "] (" + cigar.getQuantity() + ")";
							year = yearAdded;
							month = monthAdded;
							day = dayAdded;
						}
					}
				}
			}
		}
		return recent;
	}
	
	public int getNumberPerfectCigars() {
		int perfects = 0;
		
		for (Cigar cigar : getCigars())
		{
			if (cigar.getMyRating() == 10)
			{
				perfects++;
			}
		}
		return perfects;
	}
	
	public String getRandomCigar() {
		int count = 0;
		
		count = getCigars().size();
		
		Random random = new Random();
		int cigarIndex = random.nextInt(count);
		
		return getCigars().get(cigarIndex).getName();
	}
}
